package com.junan.galiao.netty;

import com.junan.galiao.enums.MessageCode;
import com.junan.galiao.pojo.dbo.User;
import com.junan.galiao.pojo.dto.Message;
import com.junan.galiao.service.UserService;
import com.junan.galiao.utils.FastJsonUtil;
import com.junan.galiao.utils.JwtTokenUtil;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author junan
 * @version V1.0
 * @className GaLiaoServerInitializer
 * @disc 尬聊 netty 服务处理类
 * @date 2020/4/24 18:16
 */
@Component
@ChannelHandler.Sharable    // 不加这个会报一个异常
public class GaLiaoHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private final UserService userService;

    // 记录和管理所有客户端的channel
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    // 尬聊号和 ChannelId 对应
    private static Map<Long, ChannelId> userChannelIds = new ConcurrentHashMap<>();

    public GaLiaoHandler(UserService userService) {
        this.userService = userService;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame twsf) throws Exception {
        // 接受的消息
        String msg = twsf.text();
        Message message = FastJsonUtil.parseObject(msg, Message.class);
        // 处理
        handle(ctx.channel(), message);
    }

    /**
     * 服务端接受客户端后的处理
     *
     * @param channel
     * @param message
     */
    private void handle(Channel channel, Message message) {
        // 验证token
        if (!checkToken(message.getToken())) {
            channel.writeAndFlush(new TextWebSocketFrame(
                    FastJsonUtil.toJSONString(
                            new Message()
                                    .code(MessageCode.FAIL.value())
                                    .data("token不合法!")
                    )
            ));
            return;
        }
        // 判断操作类型
        switch (message.getAction()) {
            // 标识自己（把自身的channel和自己的尬聊号建立联系）
            case "mark":
                markChannel(channel, message);
                break;
            // 发送消息
            case "out":
                sendMessage(message);
                break;
        }
    }

    /**
     * 把消息发送给指定的接受者
     * @param message
     */
    private void sendMessage(Message message) {
        ChannelId channelId = userChannelIds.get(message.getReceiver());
        if(channelId != null) {
            Channel channel = clients.find(channelId);
            // 对方不在线
            if(channel == null) return ;
            // 对方在线
            channel.writeAndFlush(new TextWebSocketFrame(
                    FastJsonUtil.toJSONString(
                            new Message()
                            .action("in")
                            .code(MessageCode.OK.value())
                            .sender(JwtTokenUtil.userInToken(message.getToken()).getUserId())
                            .data(message.getData())
                    )
            ));
        }
    }

    /**
     * 首次连接后标识自己，（把自身的channel和自己的尬聊号建立联系）
     * @param channel
     * @param message
     */
    private void markChannel(Channel channel, Message message) {
        // 获取登录后的尬聊号
        User user = JwtTokenUtil.userInToken(message.getToken());
        userChannelIds.put(user.getUserId(), channel.id());
    }

    /**
     * 验证token
     *
     * @param token
     * @return
     */
    private boolean checkToken(String token) {
        if (token == null || token.isEmpty())
            return false;

        return JwtTokenUtil.verifyToken(token);
    }


    /**
     * 客户端连接成功放入到 clients 中管理
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        clients.add(ctx.channel());
    }

    /**
     * 完成后移除
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    /**
     * 断开了的连接 (前端建立连接的页面关闭时)
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        clients.remove(ctx.channel());
    }
}